﻿using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Navigation;
using Microsoft.Phone.Controls;
using Microsoft.Phone.Shell;
using System.Windows.Controls;
using WindowsPhoneEssentials.Abstractions;
using WindowsPhoneMvp.Navigation.Parsing;
using WindowsPhoneMvp.Phone;

namespace WindowsPhoneMvp.Navigation
{
    /// <summary>
    /// Hooks into the Navigation events of WindowsPhone7 to perform Presenter Bindings
    /// </summary>
    public sealed class NavigationServiceContext : INavigationServiceContext, IDisposable
    {
        private readonly INavigationParser navigationParser;
        readonly IPhoneApplicationService phoneApplicationService;
        private bool released;
        private bool pendingActivation;
        readonly IDictionary<string, PageViewHost> pageViewHosts = new Dictionary<string, PageViewHost>();

        [Obsolete("Use NavigationServiceContext(INavigationParser,IPhoneApplicationFrame,IPhoneApplicationService)")]
        public NavigationServiceContext(INavigationParser navigationParser, PhoneApplicationFrame frame)
            : this(navigationParser, new PhoneApplicationFrameWrapper(frame), new PhoneApplicationServiceWrapper(PhoneApplicationService.Current))
        {
        }

        public NavigationServiceContext(
            INavigationParser navigationParser, 
            IPhoneApplicationFrame frame,
            IPhoneApplicationService phoneApplicationService)
        {
            if(navigationParser == null)
                throw new ArgumentNullException("navigationParser");
            if(frame == null)
                throw new ArgumentNullException("frame");
            if (phoneApplicationService == null) 
                throw new ArgumentNullException("phoneApplicationService");

            if(RootApplicationContext != null)
            {
                throw new NotSupportedException("NavigationServiceContext must be a singleton.");
            }

            this.navigationParser = navigationParser;
            this.phoneApplicationService = phoneApplicationService;
            RootApplicationService = phoneApplicationService;
            RootApplicationContext = frame;
        }

        public IPhoneApplicationFrame RootApplicationFrame
        {
            get { return RootApplicationContext; }
        }

        internal INavigationParser CurrentNavigationParser { get { return navigationParser; } }
        internal static IPhoneApplicationFrame RootApplicationContext { get; private set; }
        internal static IPhoneApplicationService RootApplicationService { get; private set; }

        public void Setup()
        {
            RootApplicationFrame.Navigated += Navigated;
            phoneApplicationService.Activated += Activated;
            RootApplicationFrame.JournalEntryRemoved += NavigationServiceContext_JournalEntryRemoved;
        }

        void NavigationServiceContext_JournalEntryRemoved(object sender, JournalEntryRemovedEventArgs e)
        {
            if(pageViewHosts.ContainsKey(e.Entry.Source.ToString()))
            {
                var entry = pageViewHosts[e.Entry.Source.ToString()];
                pageViewHosts.Remove(e.Entry.Source.ToString());
                entry.PresenterBinder.Release();
            }
        }

        public void Navigated(object sender, NavigationEventArgs e)
        {
            if (e.Uri.ToString() == "app://external/")
                return;

            var content = GetContentPage();
            if (PageViewHost.BindOnNagivate != null)
            {
                var host = PageViewHost.BindOnNagivate;
                host.PerformBinding();
                host.NavigationContext = this;
                host.PendingActivation = pendingActivation;
                content.Tag = new WeakReference(host);
                PageViewHost.BindOnNagivate = null;
                pendingActivation = false;

                pageViewHosts.Add(e.Uri.ToString(), host);
            }
        }

        public void Activated(object sender, ActivatedEventArgs e)
        {
            var frame = RootApplicationFrame.GetElement() as ContentControl;
            if (frame == null ||
                frame.Content == null ||
                !(frame.Content is IView))
            {
                pendingActivation = true;
            }
        }

        private Control GetContentPage()
        {
            Control content = null;
            var frame = RootApplicationFrame.GetElement() as ContentControl;
            if (frame != null &&
                frame.Content != null &&
                (frame.Content is IView))
            {
                content = (Control)frame.Content;
            }
            return content;
        }

        public void Release()
        {
            if (!released)
            {
                RootApplicationFrame.Navigated -= Navigated;
                phoneApplicationService.Activated -= Activated;
                RootApplicationFrame.JournalEntryRemoved -= NavigationServiceContext_JournalEntryRemoved;
                foreach(var pageViewHost in pageViewHosts)
                {
                    pageViewHost.Value.PresenterBinder.Release();
                }
                pageViewHosts.Clear();
                
                released = true;
            }
        }

        void IDisposable.Dispose()
        {
            Release();
        }
    }
}
